﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections;
using log4net;
using RollBack.src.ui;
using RollBackLib.src;
using RollBackLib.src.utils;

namespace RollBack.src.ui.ExplorerView
{
    /// <summary>
    /// Contains member classes which populate the left and right panels of  the ExplorerView
    /// TODO: This class and its inner class should be converted into interfaces so that it does not carry
    /// the baggage of implementation
    /// </summary>
    public class ExplorerData
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(ExplorerData));

        /// <summary>
        /// Sets the current path for drag and drop operations
        /// </summary>
        private string m_currentPath;
        public string CurrentPath
        {
            get { return m_currentPath; }
            set { m_currentPath = value; }
        }

        /// <summary>
        /// The root node displayed in the left panel
        /// </summary>
        public string RootDisplayNode
        {
            get;
            set;
        }

        public string RootDisplayNodePath
        {
            get;
            set;
        }

        public RightPanelData m_rightData;
        public LeftPanelData m_leftData;

        public ExplorerData()
        {
            m_rightData = new RightPanelData();
            m_leftData  = new LeftPanelData();
        }

        /// <summary>
        /// return true if this file will be accepted
        /// </summary>
        /// <param name="attr"></param>
        /// <returns></returns>
        public static bool acceptFileWithAttribute(FileAttributes attr)
        {
            bool accept = true;
            FileAttributes a1 = (attr & FileAttributes.System);
            FileAttributes a2 = (attr & FileAttributes.Hidden);
            if ((a1 == FileAttributes.System) || (a2 == FileAttributes.Hidden))
            {
                accept = false;
            }
            return accept;
        }


        /// <summary>
        /// Data view for the left panel
        /// </summary>
        public class LeftPanelData
        {
            /// <summary>
            /// If set to false then DND drag action is not allowed.
            /// </summary>
            public bool m_allowDrag
            {
                get;
                set;
            }

            /// <summary>
            /// If set to false then DND drop action is not allowed.
            /// </summary>
            public bool m_allowDrop
            {
                get;
                set;
            }

            /// <summary>
            /// If set to true then it allows the left tree view to have checkboxes.
            /// </summary>
            public bool m_itemCheckBoxes
            {
                get;
                set;
            }
            public LeftPanelData()
            {
                m_allowDrop = false;
                m_allowDrag = true;
                m_itemCheckBoxes = false;
            }

            /// <summary>
            /// Expand the tree node by reading the contents of the directory represented by the treenode.
            /// Return true if the operation was successful
            /// </summary>
            /// <param name="curr"></param>
            public virtual void expandNode(object data,bool isRoot,ExplorerData edata)
            {
                TreeNode curr = (TreeNode)data;
                if (isRoot)
                {
                    expandRoot(curr,edata);
                }
                else
                {
                    try
                    {
                        string rootValue = edata.RootDisplayNodePath;
                        string fullName = curr.FullPath;
                        if (fullName.Length > rootValue.Length)
                        {
                            fullName = curr.FullPath.Substring(rootValue.Length);
                        }
                        expandDir(curr,edata, fullName);
                    }
                    catch (IOException )
                    {
                        curr.Nodes.Remove(curr);
                        throw;
                    }
                }
            }

            protected virtual void expandDir(TreeNode curr,ExplorerData edata,string startDir)
            {
                // put the nodes into a local map
                IEnumerator ix = curr.Nodes.GetEnumerator();
                Dictionary<string, string> nc = new Dictionary<string, string>();
                while (ix.MoveNext())
                {
                    TreeNode tn = (TreeNode)ix.Current;
                    nc.Add(tn.Name, "");
                }
                try
                {
                    // read the information for expanding the node
                    string dir = System.IO.Path.GetFullPath(startDir);
                    if (Directory.Exists(dir))
                    {
                        string[] directories = Directory.GetDirectories(dir);
                        foreach (string s in directories)
                        {
                            DirectoryInfo dinfo = new DirectoryInfo(s);
                            if (ExplorerView.ExplorerData.acceptFileWithAttribute(dinfo.Attributes))
                            {
                                try
                                {
                                    bool removed = false;
                                    FileInfo finfo = new FileInfo(s);
                                    string key = finfo.Name + "_" + finfo.Extension + "_" + finfo.CreationTimeUtc;
                                    if (!curr.Nodes.ContainsKey(key))
                                    {
                                        TreeNode tn = curr.Nodes.Add(key, finfo.Name);
                                        tn.ImageIndex = ImageResources.Indexes.m_folderIconClosed;
                                        setItemImageIndex(tn, ThreeStateTreeView.STATE_UNCHECKED);
                                    }
                                    removed = nc.Remove(key);
                                }
                                catch (Exception e)
                                {
                                    log.Warn("Error populating " + s, e);
                                }
                            }
                        }
                    }
                    else
                    {
                        // politely tell that the directory does not exist
                        log.Debug("Directory <" + dir + "> does not exist");
                    }
                }
                catch (Exception e)
                {
                    log.Warn("Error populating " + startDir, e);
                }
                // remove the entries from the tree node which were not found in the directory
                foreach (string s in nc.Keys)
                {
                    curr.Nodes.RemoveByKey(s);
                }
            }

            private static void setItemImageIndex(TreeNode tn, int index)
            {
                tn.SelectedImageIndex = ImageResources.Indexes.m_folderIconOpen;
                //tn.StateImageIndex = index; 
            }

            protected virtual void expandRoot(object data,ExplorerData edata)
            {
                TreeNode curr = (TreeNode)data;
                // return the list of drives
                List<string> driveList = new List<string>();
                ArrayList driveHandlers = DriveHandler.GetDriveHandlers();
                foreach (var c in driveHandlers)
                {
                    DriveHandler dh = (DriveHandler)c;
                    string str = dh.GetName() + "\\";
                    TreeNode[] nodes = curr.Nodes.Find(str, false);
                    if (nodes == null || nodes.Length == 0)
                    {
                        TreeNode tn = curr.Nodes.Add(str, str);
                        setItemImageIndex(tn, ThreeStateTreeView.STATE_UNCHECKED);
                        tn.ImageIndex = ImageResources.Indexes.m_folderIconOpen;
                    }
                }
            }
        }

        /// <summary>
        /// populate the right panel
        /// </summary>
        public class RightPanelData
        {
            /// <summary>
            /// If set to false then DND drag action is not allowed.
            /// </summary>
            public bool m_allowDrag
            {
                get;
                set;
            }

            /// <summary>
            /// If set to false then DND drop action is not allowed.
            /// </summary>
            public bool m_allowDrop
            {
                get;
                set;
            }

            public bool m_itemsChecked;

            /// <summary>
            /// Flag which tells if the panel should have checkboxes for each item.
            /// </summary>
            public bool m_checkBoxes
            {
                get;
                set;
            }

            public RightPanelData()
            {
                m_allowDrag = true;
                m_allowDrop = false;
                m_itemsChecked = true;
                m_checkBoxes = true;
            }

            /// <summary>
            /// Create a ListViewItem object for a file object
            /// </summary>
            /// <param name="finfo"></param>
            /// <param name="fullPath"></param>
            /// <returns></returns>
            protected ListViewItem createItem(FileInfo finfo, bool fullPath)
            {
                string[] data = new string[4];
                data[0] = fullPath ? finfo.FullName : finfo.Name;
                data[1] = finfo.Extension;
                System.DateTime dt = finfo.LastAccessTime.ToLocalTime();
                string s = String.Format("{0} {1}",dt.ToShortTimeString(),dt.ToShortDateString());
                data[2] = s;
                data[3] = "" + finfo.Length;
                ListViewItem item = new ListViewItem(data);
                
                return item;
            }

            /// <summary>
            /// Create a ListViewItem for a Directory object
            /// </summary>
            /// <param name="dinfo"></param>
            /// <param name="fullPath"></param>
            /// <returns></returns>
            protected ListViewItem createItem(DirectoryInfo dinfo, bool fullPath)
            {
                string[] data = new string[4];
                data[0] = fullPath ? dinfo.FullName : dinfo.Name;
                data[1] = dinfo.Extension;
                System.DateTime dt = dinfo.LastAccessTime.ToLocalTime();
                string s = dt.ToShortTimeString() + " " + dt.ToShortDateString();
                data[2] = s;
                data[3] = "<DIR>";
                ListViewItem item = new ListViewItem(data);
                item.ImageIndex = ImageResources.Indexes.m_folderIconClosed;
                return item;
            }

            /// <summary>
            /// Create a ListViewItem object for a file or directory item
            /// </summary>
            /// <param name="fname">File or directory name</param>
            /// <param name="fullPath"></param>
            /// <returns></returns>
            public ListViewItem createItem(string fname,bool fullPath)
            {
                ListViewItem item = null;
                if (File.Exists(fname))
                {
                    item = createItem(new FileInfo(fname), fullPath);
                }
                else if (Directory.Exists(fname))
                {
                    item = createItem(new DirectoryInfo(fname), fullPath);
                }
                else
                {
                    string[] data = new string[4];
                    data[0] = fname;
                    data[1] = "";
                    string s = "<Unavailable>";
                    data[2] = s;
                    data[3] = "<Unknown>";
                    item = new ListViewItem(data);
                }
                item.Checked = this.m_checkBoxes;
                return item;
            }


            /// <summary>
            /// Populate the right panel with Directories and files
            /// </summary>
            /// <param name="dir"></param>
            public virtual List<ListViewItem> expandNode(ExplorerData edata,string dir,bool checkAll)
            {
                string[] fileNames = Directory.GetFiles(dir);
                string[] dirs = Directory.GetDirectories(dir);
                DateTime start = DateTime.Now;
                // populate the directory names first.
                List<ListViewItem> items = new List<ListViewItem>();
                foreach (string s in dirs)
                {
                    try
                    {
                        DirectoryInfo finfo = new DirectoryInfo(s);
                        if (ExplorerView.ExplorerData.acceptFileWithAttribute(finfo.Attributes))
                        {
                            ListViewItem item = createItem(finfo, false);
                            item.Checked = checkAll;
                            items.Add(item);
                        }
                    }
                    catch (Exception e)
                    {
                        log.Warn("Error populating the right panel with directory <" + s + ">", e);
                    }
                }

                // populate the file names
                foreach (string s in fileNames)
                {
                    try
                    {
                        FileInfo finfo = new FileInfo(s);
                        if (ExplorerView.ExplorerData.acceptFileWithAttribute(finfo.Attributes))
                        {
                            ListViewItem item = createItem(finfo, false);
                            item.Checked = checkAll;
                            items.Add(item);
                        }
                    }
                    catch (Exception e)
                    {
                        log.Warn("Error populating the right panel with file <" + s + ">", e);
                    }
                }
                return items;
            }

            public virtual string getPathOnDisk(ExplorerData edata)
            {
                return "";
            }

        }
    }
}
